/**
 * Contains all the methods to convert DB-format values to UOM-format values (and vice versa)
 *
 * @author remo.lemma@lu.unisi.ch
 */

package ch.unisi.inf.projects.java.uom;

// Libraries
import java.util.Set;
import java.util.Map;
import java.util.HashMap;

import ch.unisi.inf.projects.java.uom.exceptions.UOMException;
import ch.unisi.inf.projects.java.uom.exceptions.UOMExceptions;

public final class Parser {
	
	/** The only instance of Parser class */
	private static Parser instance = null;
	
	/**
	 * A parser cannot be creates - it's a singleton
	 */
	private Parser()
	{
	}
	
	/**
	 * Returns the instance of the Parser class
	 * @return the parser
	 */
	public static Parser instance()
	{
		if (instance == null)
			instance = new Parser();
		return instance;
	}
	
	/**
	 * Returns a Pair (relationship - variable) representing a list type
	 * @param field the encoded list type field
	 * @return the pair of values or a null pair
	 */
	private Pair<String,String> dbParseListType (final String field) throws UOMException
	{
		if (field == null || field.equals(Constants.ITEM_PRIMARY_KEY.value()))
			return new Pair<String,String>(null,null);
		final String[] splits = field.split(Constants.ITEM_PARSECHAR.value());
		if (splits.length < 2)
			throw new UOMException(UOMExceptions.MALFORMED_DATA.code(), "Malformed data detected");
		final String listType = splits[0];
		final int length = Constants.ITEM_CLASS_LIST.value().length();
		if (listType == null || listType.length() <= length || !listType.substring(0,length).toUpperCase().equals(Constants.ITEM_CLASS_LIST.value().toUpperCase()))
			return new Pair<String,String>(null,null);
		String var = new String();
		for (int i = 1; i < splits.length; i++)
			var += splits[i] + Constants.ITEM_PARSECHAR.value();
		return new Pair<String,String>(listType.substring(length).toUpperCase(),var.substring(0,var.length() - 1).replace(Constants.ITEM_PARSECHAR.value(), " "));
	}
	
	/**
	 * Returns the relation of the list type
	 * @param field the list type field
	 * @return the relation or null
	 * @throws UOMException handles the needed exceptions
	 */
	public String dbGetListTypeRelationship (final String field) throws UOMException
	{
		if (field == null)
			return null;
		return this.dbParseListType(field).getLeft();
	}
	
	/**
	 * Returns the variable of the list type field
	 * @param field the list type field
	 * @return the name of the variable or null
	 * @throws UOMException handles the needed exceptions
	 */
	public String dbGetListTypeVar (final String field) throws UOMException
	{
		if (field == null)
			return null;
		return this.dbParseListType(field).getRight();
	}
		
	/**
	 * Checks if the given type is a list type
	 * @param listType the type to check
	 * @return true if it's a list type or false otherwise
	 */
	public boolean dbColumnTypeIsList (final String listType)
	{
		final int length = Constants.ITEM_CLASS_LIST.value().length();
		if (listType == null || listType.length() <= length || !listType.substring(0,length).equals(Constants.ITEM_CLASS_LIST.value()))
			return false;
		return true;
	}
	
	/**
	 * Converts an UOM-formatted Set of fields into a Map of relation tables that maps field name to relation
	 * @param columnInput the UOM-formatted Map of fields
	 * @return a map of field-to-relation
	 */
	public Map<String,String> dbPrepareRelationshipTables (final Set<String> columnInput) throws UOMException
	{
		final Map<String,String> tables = new HashMap<String,String>();
		for (String column : columnInput) {
			final String relationship = this.dbGetListTypeRelationship(column);
			if (relationship != null)
				tables.put(this.dbGetListTypeVar(column),relationship);
		}
		return tables;
	}
	
	/**
	 * Builds a compatible table name
	 * @param cleanName the not prefixed table name
	 * @return the prefixed table name
	 */
	public String dbPrepareTableName (final String cleanName)
	{
		return Constants.ITEM_TABLE_PREFIX.value() + cleanName;
	}
	
	/**
	 * Encodes the relationship table name
	 * @param origin the table of origin
	 * @param relField the field representing the relationship
	 * @return the name of the relationship table
	 * @throws UOMException handles the needed exceptions
	 */
	public String dbPrepareRelTableName (final String origin, final String relField) throws UOMException
	{
		final String div = Constants.ITEM_PARSECHAR.value();
		return dbPrepareTableName(origin) + div + dbGetListTypeRelationship(relField) + div + dbGetListTypeVar(relField);
	}
	
	/**
	 * Parses the field name from the relationship table name
	 * @param relTable the name of the relationship table
	 * @return the name of the field
	 * @throws UOMException handles the needed exceptions
	 */
	public String dbParseRelTableName (final String relTable) throws UOMException
	{
		final String[] splits = relTable.split(Constants.ITEM_PARSECHAR.value());
		if (splits.length < 3)
			throw new UOMException(UOMExceptions.MALFORMED_DATA.code(), "Malformed data detected");
		return Constants.ITEM_CLASS_LIST.value() + splits[1] + Constants.ITEM_PARSECHAR.value() + splits[2];
	}
	
	/**
	 * Converts a database formatted Map of relationship values in a UOM-formatted one
	 * @param relValues the Map of database formatted relationship values
	 * @return a map of UOM-formatted relationship values
	 */
	public Map<String,String> dbParseRelValues (final Map<String,String> relValues)
	{
		final Map<String,String> newValues = new HashMap<String,String>();
		for (final String key : relValues.keySet()) {
			final String[] splits = relValues.get(key).split(Constants.ITEM_PARSECHAR.value());
			int index = 0;
			for (String id : splits)
				newValues.put(key + Constants.ITEM_PARSECHAR.value() + id + Constants.ITEM_PARSECHAR.value() + index++, id);
		}
		return newValues;
	}
	
	/**
	 * Returns the type of the given variable
	 * @param var the variable
	 * @return the type of the given variable
	 * @throws UOMException handles the needed exceptions
	 */
	public String dbGetVarType (final String var) throws UOMException
	{
		final String[] splits = var.split(Constants.ITEM_PARSECHAR.value());
		if (splits.length < 2)
			throw new UOMException(UOMExceptions.MALFORMED_DATA.code(), "Malformed data detected");
		if (this.dbColumnTypeIsList(var))
			return this.dbGetListTypeRelationship(var);
		else
			return splits[0];
	}
	
	/**
	 * Returns the name of the given variable
	 * @param var the variable
	 * @return the name of the given variable
	 * @throws UOMException handles the needed exceptions
	 */
	public String dbGetVarName (final String var) throws UOMException
	{
		if (Constants.ITEM_PRIMARY_KEY.value().toUpperCase().equals(var.toUpperCase()))
			return var;
		final String[] splits = var.split(Constants.ITEM_PARSECHAR.value());
		if (splits.length < 2)
			throw new UOMException(UOMExceptions.MALFORMED_DATA.code(), "Malformed data detected");
		if (this.dbColumnTypeIsList(var))
			return this.dbGetListTypeVar(var);
		else {
			String name = new String();
			for (int i = 1; i < splits.length; i++)
				name += splits[i] + " ";
			return name.toUpperCase().substring(0, name.length() - 1);
		}
	}
}